Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / service / src / jvmMain / kotlin / org / meshtastic / core / service / LockdownPassphraseStoreImpl.kt

Displaying Raw • Download

core/service/src/jvmMain/kotlin/org/meshtastic/core/service/LockdownPassphraseStoreImpl.kt bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 8.14 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.service

Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787org.koin.core.annotation.Single
Tff7b72import T7ee787org.meshtastic.core.database.desktopDataDir
Tff7b72import T7ee787org.meshtastic.core.repository.LockdownPassphraseStore
Tff7b72import T7ee787org.meshtastic.core.repository.StoredPassphrase
Tff7b72import T7ee787java.io.File
Tff7b72import T7ee787java.io.FileInputStream
Tff7b72import T7ee787java.io.FileOutputStream
Tff7b72import T7ee787java.security.KeyStore
Tff7b72import T7ee787javax.crypto.Cipher
Tff7b72import T7ee787javax.crypto.KeyGenerator
Tff7b72import T7ee787javax.crypto.SecretKey
Tff7b72import T7ee787javax.crypto.spec.GCMParameterSpec

T8b949e/**
* File-backed encrypted passphrase store for JVM/Desktop.
*
* Uses a PKCS12 KeyStore to hold an AES-256 master key and AES-256-GCM to encrypt each passphrase entry. Entries are
* stored as individual `.enc` files under `$MESHTASTIC_DATA_DIR/lockdown/` (default: `~/.meshtastic/lockdown/`), keyed
* by a sanitized device address.
*
* The keystore password is fixed because the threat model mirrors Android's `EncryptedSharedPreferences`: file-system
* permission is the primary access control; the encryption layer protects data at rest against casual file browsing or
* backup leakage, not against a compromised user account.
*/
Tf0883e@SingleTb4b4b4(Te6edf3binds Tff7b72= Tff7b72[Te6edf3LockdownPassphraseStoreTff7b72::Te6edf3classTff7b72]Tb4b4b4)
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4)
Tff7b72class T56d364LockdownPassphraseStoreImpl Tb4b4b4: Te6edf3LockdownPassphraseStore Tb4b4b4{

Tff7b72private Tff7b72val Te6edf3lockdownDirTb4b4b4: Te6edf3File Tff7b72by Te6edf3lazy Tb4b4b4{ Te6edf3FileTb4b4b4(Te6edf3desktopDataDirTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3LOCKDOWN_DIRTb4b4b4)Tb4b4b4.Te6edf3also Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3mkdirsTb4b4b4(Tb4b4b4) Tb4b4b4} Tb4b4b4}

Tff7b72private Tff7b72val Te6edf3masterKeyTb4b4b4: Te6edf3SecretKey? Tff7b72by Te6edf3lazy Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3loadOrCreateMasterKeyTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffLockdown: Failed to initialize desktop keystoreTa5d6ff" Tb4b4b4}
Tff7b72null
Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffgetPassphraseTb4b4b4(Te6edf3deviceAddressTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3StoredPassphrase? Tb4b4b4{
Tff7b72val Te6edf3key Tff7b72= Te6edf3masterKey Tff7b72?: Tff7b72return Tff7b72null
Tff7b72val Te6edf3file Tff7b72= Te6edf3entryFileTb4b4b4(Te6edf3deviceAddressTb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3fileTb4b4b4.Te6edf3existsTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72return Tff7b72null
Tff7b72return Tff7b72try Tb4b4b4{
Tff7b72val Te6edf3encrypted Tff7b72= Te6edf3fileTb4b4b4.Te6edf3readBytesTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3plaintext Tff7b72= Te6edf3decryptTb4b4b4(Te6edf3keyTb4b4b4, Te6edf3encryptedTb4b4b4)
Te6edf3deserializeTb4b4b4(Te6edf3plaintextTb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffLockdown: Failed to read passphrase for deviceTa5d6ff" Tb4b4b4}
Tff7b72null
Tb4b4b4}
Tb4b4b4}

Tff7b72override Tff7b72fun Td2a8ffsavePassphraseTb4b4b4(
Te6edf3deviceAddressTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3passphraseTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3bootsTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3hoursTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3maxSessionSecondsTb4b4b4: Tffa657IntTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3key Tff7b72= Te6edf3masterKey Tff7b72?: Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6ffLockdown: Cannot save passphrase - keystore unavailableTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3plaintext Tff7b72= Te6edf3serializeTb4b4b4(Te6edf3passphraseTb4b4b4, Te6edf3bootsTb4b4b4, Te6edf3hoursTb4b4b4, Te6edf3maxSessionSecondsTb4b4b4)
Tff7b72val Te6edf3encrypted Tff7b72= Te6edf3encryptTb4b4b4(Te6edf3keyTb4b4b4, Te6edf3plaintextTb4b4b4)
Te6edf3entryFileTb4b4b4(Te6edf3deviceAddressTb4b4b4)Tb4b4b4.Te6edf3writeBytesTb4b4b4(Te6edf3encryptedTb4b4b4)
Tb4b4b4}

Tff7b72override Tff7b72fun Td2a8ffclearPassphraseTb4b4b4(Te6edf3deviceAddressTb4b4b4: Tffa657StringTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3file Tff7b72= Te6edf3entryFileTb4b4b4(Te6edf3deviceAddressTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3fileTb4b4b4.Te6edf3existsTb4b4b4(Tb4b4b4) Tff7b72&Tff7b72& Tff7b72!Te6edf3fileTb4b4b4.Te6edf3deleteTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffLockdown: Passphrase file was not deleted for deviceTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffentryFileTb4b4b4(Te6edf3deviceAddressTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3File Tb4b4b4{
Tff7b72val Te6edf3sanitized Tff7b72= Te6edf3deviceAddressTb4b4b4.Te6edf3replaceTb4b4b4(Te6edf3RegexTb4b4b4(Ta5d6ff"Ta5d6ff[^a-zA-Z0-9_-]Ta5d6ff"Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ff_Ta5d6ff"Tb4b4b4)
Tff7b72return Te6edf3FileTb4b4b4(Te6edf3lockdownDirTb4b4b4, Ta5d6ff"Tffd700$Te6edf3sanitizedTa5d6ff.encTa5d6ff"Tb4b4b4)
Tb4b4b4}

T8b949e// region Encryption

Tff7b72private Tff7b72fun Td2a8ffencryptTb4b4b4(Te6edf3keyTb4b4b4: Te6edf3SecretKeyTb4b4b4, Te6edf3plaintextTb4b4b4: Te6edf3ByteArrayTb4b4b4)Tb4b4b4: Te6edf3ByteArray Tb4b4b4{
Tff7b72val Te6edf3cipher Tff7b72= Te6edf3CipherTb4b4b4.Te6edf3getInstanceTb4b4b4(Te6edf3AES_GCM_TRANSFORMTb4b4b4)
Te6edf3cipherTb4b4b4.Te6edf3initTb4b4b4(Te6edf3CipherTb4b4b4.Te6edf3ENCRYPT_MODETb4b4b4, Te6edf3keyTb4b4b4)
Tff7b72val Te6edf3iv Tff7b72= Te6edf3cipherTb4b4b4.Te6edf3iv
Tff7b72val Te6edf3ciphertext Tff7b72= Te6edf3cipherTb4b4b4.Te6edf3doFinalTb4b4b4(Te6edf3plaintextTb4b4b4)
T8b949e// Format: [1 byte IV length][IV][ciphertext]
Tff7b72return Te6edf3byteArrayOfTb4b4b4(Te6edf3ivTb4b4b4.Te6edf3sizeTb4b4b4.Te6edf3toByteTb4b4b4(Tb4b4b4)Tb4b4b4) Tff7b72+ Te6edf3iv Tff7b72+ Te6edf3ciphertext
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffdecryptTb4b4b4(Te6edf3keyTb4b4b4: Te6edf3SecretKeyTb4b4b4, Te6edf3dataTb4b4b4: Te6edf3ByteArrayTb4b4b4)Tb4b4b4: Te6edf3ByteArray Tb4b4b4{
Tff7b72val Te6edf3ivLength Tff7b72= Te6edf3dataTff7b72[T79c0ff0Tff7b72]Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4) Te6edf3and Te6edf3BYTE_MASK
Tff7b72val Te6edf3iv Tff7b72= Te6edf3dataTb4b4b4.Te6edf3copyOfRangeTb4b4b4(T79c0ff1Tb4b4b4, T79c0ff1 Tff7b72+ Te6edf3ivLengthTb4b4b4)
Tff7b72val Te6edf3ciphertext Tff7b72= Te6edf3dataTb4b4b4.Te6edf3copyOfRangeTb4b4b4(T79c0ff1 Tff7b72+ Te6edf3ivLengthTb4b4b4, Te6edf3dataTb4b4b4.Te6edf3sizeTb4b4b4)
Tff7b72val Te6edf3cipher Tff7b72= Te6edf3CipherTb4b4b4.Te6edf3getInstanceTb4b4b4(Te6edf3AES_GCM_TRANSFORMTb4b4b4)
Te6edf3cipherTb4b4b4.Te6edf3initTb4b4b4(Te6edf3CipherTb4b4b4.Te6edf3DECRYPT_MODETb4b4b4, Te6edf3keyTb4b4b4, Te6edf3GCMParameterSpecTb4b4b4(Te6edf3GCM_TAG_BITSTb4b4b4, Te6edf3ivTb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3cipherTb4b4b4.Te6edf3doFinalTb4b4b4(Te6edf3ciphertextTb4b4b4)
Tb4b4b4}

T8b949e// endregion

T8b949e// region Serialization (simple line-based to avoid adding kotlinx-serialization dependency)

T8b949e// Format v2: "boots\nhours\nmaxSessionSeconds\npassphrase" (4 lines).
T8b949e// Backward-compat: legacy 3-line entries (no maxSessionSeconds) decode with maxSessionSeconds=0.
Tff7b72private Tff7b72fun Td2a8ffserializeTb4b4b4(Te6edf3passphraseTb4b4b4: Tffa657StringTb4b4b4, Te6edf3bootsTb4b4b4: Tffa657IntTb4b4b4, Te6edf3hoursTb4b4b4: Tffa657IntTb4b4b4, Te6edf3maxSessionSecondsTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3ByteArray Tff7b72=
Ta5d6ff"Tffd700$Te6edf3bootsTa5d6ff\Ta5d6ffnTffd700$Te6edf3hoursTa5d6ff\Ta5d6ffnTffd700$Te6edf3maxSessionSecondsTa5d6ff\Ta5d6ffnTffd700$Te6edf3passphraseTa5d6ff"Tb4b4b4.Te6edf3encodeToByteArrayTb4b4b4(Tb4b4b4)

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffdeserializeTb4b4b4(Te6edf3plaintextTb4b4b4: Te6edf3ByteArrayTb4b4b4)Tb4b4b4: Te6edf3StoredPassphrase? Tb4b4b4{
Tff7b72val Te6edf3text Tff7b72= Te6edf3plaintextTb4b4b4.Te6edf3decodeToStringTb4b4b4(Tb4b4b4)
T8b949e// Try v2 (4-line) format first.
Tff7b72val Te6edf3v2 Tff7b72= Te6edf3textTb4b4b4.Te6edf3splitTb4b4b4(Ta5d6ff"Ta5d6ff\Ta5d6ffnTa5d6ff"Tb4b4b4, Te6edf3limit Tff7b72= T79c0ff4Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3v2Tb4b4b4.Te6edf3size Tff7b72=Tff7b72= Te6edf3SERIALIZED_LINE_COUNT_V2Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3boots Tff7b72= Te6edf3v2Tff7b72[T79c0ff0Tff7b72]Tb4b4b4.Te6edf3toIntOrNullTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3hours Tff7b72= Te6edf3v2Tff7b72[T79c0ff1Tff7b72]Tb4b4b4.Te6edf3toIntOrNullTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3maxSession Tff7b72= Te6edf3v2Tff7b72[T79c0ff2Tff7b72]Tb4b4b4.Te6edf3toIntOrNullTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3boots Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3hours Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3maxSession Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3StoredPassphraseTb4b4b4(
Te6edf3passphrase Tff7b72= Te6edf3v2Tff7b72[T79c0ff3Tff7b72]Tb4b4b4,
Te6edf3boots Tff7b72= Te6edf3bootsTb4b4b4,
Te6edf3hours Tff7b72= Te6edf3hoursTb4b4b4,
Te6edf3maxSessionSeconds Tff7b72= Te6edf3maxSessionTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e// Fall back to v1 (3-line, no maxSessionSeconds).
Tff7b72val Te6edf3v1 Tff7b72= Te6edf3textTb4b4b4.Te6edf3splitTb4b4b4(Ta5d6ff"Ta5d6ff\Ta5d6ffnTa5d6ff"Tb4b4b4, Te6edf3limit Tff7b72= T79c0ff3Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3v1Tb4b4b4.Te6edf3size Tff7b72< Te6edf3SERIALIZED_LINE_COUNT_V1Tb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffLockdown: Invalid passphrase entry formatTa5d6ff" Tb4b4b4}
Tff7b72return Tff7b72null
Tb4b4b4}
Tff7b72val Te6edf3boots Tff7b72= Te6edf3v1Tff7b72[T79c0ff0Tff7b72]Tb4b4b4.Te6edf3toIntOrNullTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3hours Tff7b72= Te6edf3v1Tff7b72[T79c0ff1Tff7b72]Tb4b4b4.Te6edf3toIntOrNullTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3boots Tff7b72=Tff7b72= Tff7b72null Tff7b72|Tff7b72| Te6edf3hours Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffLockdown: Invalid passphrase entry metadataTa5d6ff" Tb4b4b4}
Tff7b72return Tff7b72null
Tb4b4b4}
Tff7b72return Te6edf3StoredPassphraseTb4b4b4(Te6edf3passphrase Tff7b72= Te6edf3v1Tff7b72[T79c0ff2Tff7b72]Tb4b4b4, Te6edf3boots Tff7b72= Te6edf3bootsTb4b4b4, Te6edf3hours Tff7b72= Te6edf3hoursTb4b4b4)
Tb4b4b4}

T8b949e// endregion

T8b949e// region KeyStore

Tff7b72private Tff7b72fun Td2a8ffloadOrCreateMasterKeyTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3SecretKey Tb4b4b4{
Tff7b72val Te6edf3ksFile Tff7b72= Te6edf3FileTb4b4b4(Te6edf3lockdownDirTb4b4b4, Te6edf3KEYSTORE_FILETb4b4b4)
Tff7b72val Te6edf3ks Tff7b72= Te6edf3KeyStoreTb4b4b4.Te6edf3getInstanceTb4b4b4(Te6edf3KEYSTORE_TYPETb4b4b4)
Tff7b72val Te6edf3protection Tff7b72= Te6edf3KeyStoreTb4b4b4.Te6edf3PasswordProtectionTb4b4b4(Te6edf3KEYSTORE_PASSWORDTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3ksFileTb4b4b4.Te6edf3existsTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3FileInputStreamTb4b4b4(Te6edf3ksFileTb4b4b4)Tb4b4b4.Te6edf3use Tb4b4b4{ Te6edf3ksTb4b4b4.Te6edf3loadTb4b4b4(Tffa657itTb4b4b4, Te6edf3KEYSTORE_PASSWORDTb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3entry Tff7b72= Te6edf3ksTb4b4b4.Te6edf3getEntryTb4b4b4(Te6edf3KEY_ALIASTb4b4b4, Te6edf3protectionTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3entry Tff7b72is Te6edf3KeyStoreTb4b4b4.Te6edf3SecretKeyEntryTb4b4b4) Tff7b72return Te6edf3entryTb4b4b4.Te6edf3secretKey
Tb4b4b4}
T8b949e// Generate new master key
Tff7b72val Te6edf3keyGen Tff7b72= Te6edf3KeyGeneratorTb4b4b4.Te6edf3getInstanceTb4b4b4(Te6edf3AES_ALGORITHMTb4b4b4)
Te6edf3keyGenTb4b4b4.Te6edf3initTb4b4b4(Te6edf3AES_KEY_BITSTb4b4b4)
Tff7b72val Te6edf3secretKey Tff7b72= Te6edf3keyGenTb4b4b4.Te6edf3generateKeyTb4b4b4(Tb4b4b4)
Te6edf3ksTb4b4b4.Te6edf3loadTb4b4b4(Tff7b72nullTb4b4b4, Te6edf3KEYSTORE_PASSWORDTb4b4b4)
Te6edf3ksTb4b4b4.Te6edf3setEntryTb4b4b4(Te6edf3KEY_ALIASTb4b4b4, Te6edf3KeyStoreTb4b4b4.Te6edf3SecretKeyEntryTb4b4b4(Te6edf3secretKeyTb4b4b4)Tb4b4b4, Te6edf3protectionTb4b4b4)
Te6edf3FileOutputStreamTb4b4b4(Te6edf3ksFileTb4b4b4)Tb4b4b4.Te6edf3use Tb4b4b4{ Te6edf3ksTb4b4b4.Te6edf3storeTb4b4b4(Tffa657itTb4b4b4, Te6edf3KEYSTORE_PASSWORDTb4b4b4) Tb4b4b4}
Tff7b72return Te6edf3secretKey
Tb4b4b4}

T8b949e// endregion

Tff7b72private Tff7b72companion Tff7b72object Tb4b4b4{
Tff7b72private Tff7b72const Tff7b72val Te6edf3LOCKDOWN_DIR Tff7b72= Ta5d6ff"Ta5d6fflockdownTa5d6ff"
Tff7b72private Tff7b72const Tff7b72val Te6edf3KEYSTORE_FILE Tff7b72= Ta5d6ff"Ta5d6ffkeystore.p12Ta5d6ff"
Tff7b72private Tff7b72const Tff7b72val Te6edf3KEYSTORE_TYPE Tff7b72= Ta5d6ff"Ta5d6ffPKCS12Ta5d6ff"
Tff7b72private Tff7b72const Tff7b72val Te6edf3KEY_ALIAS Tff7b72= Ta5d6ff"Ta5d6fflockdown_masterTa5d6ff"

T8b949e// Intentional: this mirrors the documented desktop threat model for at-rest protection only.
Tff7b72private Tff7b72val Te6edf3KEYSTORE_PASSWORD Tff7b72= Ta5d6ff"Ta5d6ffmeshtastic-lockdownTa5d6ff"Tb4b4b4.Te6edf3toCharArrayTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72const Tff7b72val Te6edf3AES_ALGORITHM Tff7b72= Ta5d6ff"Ta5d6ffAESTa5d6ff"
Tff7b72private Tff7b72const Tff7b72val Te6edf3AES_GCM_TRANSFORM Tff7b72= Ta5d6ff"Ta5d6ffAES/GCM/NoPaddingTa5d6ff"
Tff7b72private Tff7b72const Tff7b72val Te6edf3AES_KEY_BITS Tff7b72= T79c0ff2T79c0ff5T79c0ff6
Tff7b72private Tff7b72const Tff7b72val Te6edf3GCM_TAG_BITS Tff7b72= T79c0ff1T79c0ff2T79c0ff8
Tff7b72private Tff7b72const Tff7b72val Te6edf3BYTE_MASK Tff7b72= T79c0ff0Te6edf3xFF
Tff7b72private Tff7b72const Tff7b72val Te6edf3SERIALIZED_LINE_COUNT_V1 Tff7b72= T79c0ff3
Tff7b72private Tff7b72const Tff7b72val Te6edf3SERIALIZED_LINE_COUNT_V2 Tff7b72= T79c0ff4
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.07s